package com.ljx.itbbs.server.article.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ljx.itbbs.common.domain.entity.UserEntity;
import com.ljx.itbbs.common.domain.enums.CommonStatusEnum;
import com.ljx.itbbs.common.domain.model.LoginUser;
import com.ljx.itbbs.common.domain.vo.resp.PageResp;
import com.ljx.itbbs.common.exception.BusinessException;
import com.ljx.itbbs.common.manager.AsyncManager;
import com.ljx.itbbs.common.utils.AssertUtil;
import com.ljx.itbbs.common.utils.SecurityUtil;
import com.ljx.itbbs.common.utils.ServletUtil;
import com.ljx.itbbs.common.utils.SnowFlakeUtil;
import com.ljx.itbbs.server.article.dao.ArticleDao;
import com.ljx.itbbs.server.article.domain.entity.*;
import com.ljx.itbbs.server.article.domain.enums.CollectEnum;
import com.ljx.itbbs.server.article.domain.enums.LikeEnum;
import com.ljx.itbbs.server.article.domain.vo.req.*;
import com.ljx.itbbs.server.article.domain.vo.resp.ArticleInfoResp;
import com.ljx.itbbs.server.article.domain.vo.resp.ArticleUpdateInfoResp;
import com.ljx.itbbs.server.article.domain.vo.resp.ResourcesResp;
import com.ljx.itbbs.server.article.event.*;
import com.ljx.itbbs.server.article.service.*;
import com.ljx.itbbs.server.article.service.adapter.ArticleAdapter;
import com.ljx.itbbs.server.article.service.handler.ArticleAsyncHandler;
import com.ljx.itbbs.server.user.domain.entity.UserPlateRelationEntity;
import com.ljx.itbbs.server.user.service.UserPlateRelationService;
import com.ljx.itbbs.server.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleDao articleDao;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private UserService userService;

    @Autowired
    private LikeRecordService likeRecordService;

    @Autowired
    private CollectRecordService collectRecordService;

    @Autowired
    private ResourceService resourceService;

    @Autowired
    private ResourceUserRelationService resourceUserRelationService;

    @Autowired
    private ArticleDraftService articleDraftService;

    @Autowired
    private UserPlateRelationService userPlateRelationService;

    @Override
    @Transactional
    public Long postArticle(Long userId, ArticleSaveReq req) {
        ArticleEntity article = ArticleAdapter.buildArticleSave(req, userId);
        // 保存文章
        articleDao.save(article);
        // 保存文章标签，增加分类文章数
        applicationEventPublisher.publishEvent(
                new ArticlePostEvent(this, article.getId(), req.getTagList(), req.getCategoryId(), userId,
                        req.getResourceList(), req.getIntegral(), req.getIsFree())
        );

        return article.getId();
    }

    @Override
    public PageResp<ArticleInfoResp> getArticleList(LoginUser loginUser, ArticleListReq req) {
        // 若是匿名用户需要查看是否有权限访问
        if (Objects.nonNull(req.getStatus())
                && (Objects.isNull(loginUser) || !req.getUid().equals(loginUser.getUserId()))){
            throw new BusinessException("无权限");
        }
        IPage<ArticleEntity> iPageList = articleDao.getArticleList(req);
        if (iPageList.getTotal() == 0){
            return new PageResp<ArticleInfoResp>().empty();
        }

        return handleIPageList(iPageList);
    }

    private PageResp<ArticleInfoResp> handleIPageList(IPage<ArticleEntity> pageResp) {
        List<ArticleEntity> articleList = pageResp.getRecords();
        Map<Long, UserEntity> userMap = getUserMap(articleList);
        List<ArticleInfoResp> records = articleList.stream().map(item -> {
            ArticleInfoResp res = BeanUtil.copyProperties(item, ArticleInfoResp.class);
            res.setTags(JSONUtil.toList(item.getTags(), String.class));
            UserEntity user = userMap.get(item.getUid());
            return articleRespMergeUser(res, user);
        }).collect(Collectors.toList());

        return PageResp.<ArticleInfoResp>builder()
                .list(records)
                .total(pageResp.getTotal())
                .build();
    }

    @Override
    public ArticleInfoResp getArticleInfo(ArticleInfoReq req) {
        // 文章信息
        ArticleEntity article = articleDao.getById(req.getArticleId());
        // 是否下架或者不存在
        AssertUtil.isNotEmpty(article, "文章不存在");
        AssertUtil.isFalse(article.getStatus().equals(CommonStatusEnum.ERROR.getStatus()), "文章已下架");

        ArticleInfoResp articleInfoResp = BeanUtil.copyProperties(article, ArticleInfoResp.class);
        articleInfoResp.setTags(JSONUtil.toList(article.getTags(), String.class));
        articleInfoResp.setResources(getArticleResources(req.getArticleId()));
        // 用户信息
        UserEntity user = userService.getUserInfoById(article.getUid());
        // 异步更新浏览量
        AsyncManager.me().execute(ArticleAsyncHandler.increaseViewsNum(req.getArticleId()));

        return articleRespMergeUser(articleInfoResp, user);
    }

    private List<ResourcesResp> getArticleResources(Long articleId) {
        List<ResourceEntity> resourceList = resourceService.getByArticleId(articleId);
        if (resourceList.isEmpty()){
            // 无数据直接返回
            return new ArrayList<>();
        }
        if(ServletUtil.getAuthentication().getPrincipal().equals(SecurityUtil.NOT_LOGIN)){
            // 未登录直接返回
            return resourceList.stream().map(item -> {
                ResourcesResp resp = BeanUtil.copyProperties(item, ResourcesResp.class);
                resp.setHasPerms(false);
                return resp;
            }).collect(Collectors.toList());
        }
        // 获取资源信息
        List<Long> resIds = resourceList
                .stream()
                .map(ResourceEntity::getId)
                .collect(Collectors.toList());
        List<ResourceUserRelationEntity> resourceUserList = resourceUserRelationService.batchResourceUser(ServletUtil.getUserId(), resIds);
        Map<Long, ResourceUserRelationEntity> resourceUserMap = resourceUserList.stream()
                .collect(Collectors.toMap(ResourceUserRelationEntity::getResourceId, Function.identity()));

        return resourceList.stream().map(item -> {
            ResourcesResp resp = BeanUtil.copyProperties(item, ResourcesResp.class);
            resp.setHasPerms(Objects.nonNull(resourceUserMap.get(item.getId())));
            return resp;
        }).collect(Collectors.toList());
    }

    private ArticleInfoResp articleRespMergeUser(ArticleInfoResp articleInfoResp, UserEntity user) {
        articleInfoResp.setNickname(user.getNickname());
        articleInfoResp.setAvatar(user.getAvatar());
        return articleInfoResp;
    }

    @Override
    public List<ArticleInfoResp> getNewArticleInfo(ArticleNewReq req) {
        List<ArticleEntity> newArticleList = articleDao.getNewArticleInfo(req);
        Map<Long, UserEntity> userMap = getUserMap(newArticleList);

        return newArticleList.stream().map(item -> {
            ArticleInfoResp articleInfoResp = BeanUtil.copyProperties(item, ArticleInfoResp.class);
            UserEntity user = userMap.get(item.getUid());
            articleInfoResp.setNickname(user.getNickname());
            articleInfoResp.setAvatar(user.getAvatar());
            return articleInfoResp;
        }).collect(Collectors.toList());
    }

    @Override
    public List<ArticleInfoResp> getHotArticleInfo() {
        List<ArticleEntity> hotArticleList = articleDao.getHotArticleInfo();
        Map<Long, UserEntity> userMap = getUserMap(hotArticleList);

        return hotArticleList.stream().map(item -> {
            ArticleInfoResp articleInfoResp = BeanUtil.copyProperties(item, ArticleInfoResp.class);
            UserEntity user = userMap.get(item.getUid());
            articleInfoResp.setNickname(user.getNickname());
            articleInfoResp.setAvatar(user.getAvatar());
            return articleInfoResp;
        }).collect(Collectors.toList());
    }

    /**
     * 获取用户Map
     * @param articleList
     * @return
     */
    private Map<Long, UserEntity> getUserMap(List<ArticleEntity> articleList) {
        Set<Long> userIds = articleList.stream().map(ArticleEntity::getUid).collect(Collectors.toSet());
        return userService.getUserInfoByIds(new ArrayList<>(userIds));
    }

    @Override
    public void increaseViewsNum(Long articleId) {
        articleDao.increaseViewsNum(articleId);
    }

    @Override
    public void increaseCommentNum(Long articleId) {
        articleDao.increaseCommentNum(articleId);
    }

    @Override
    public void likeArticle(Long userId, Long articleId) {
        // 查询是否已经点赞过了
        String objKey = LikeEnum.getObjKey(LikeEnum.ARTICLE, articleId.toString());
        LikeRecordEntity likeRecord = likeRecordService.getLikeRecord(objKey, userId);
        if (Objects.nonNull(likeRecord)){
            // 取消点赞和删除记录
            applicationEventPublisher.publishEvent(
                    new DislikeEvent(this, likeRecord.getUserId(), likeRecord.getLikeObj(), likeRecord.getType(),
                            articleId)
            );
            throw new BusinessException("取消点赞");
        }

        // 更新点赞和保存记录
        applicationEventPublisher.publishEvent(
                new LikeEvent(this, objKey, userId, articleId, LikeEnum.ARTICLE.getType()));
    }

    @Override
    public void collectArticle(Long userId, Long articleId) {
        // 查询是否已经收藏过了
        String key = CollectEnum.getObjKey(CollectEnum.ARTICLE, articleId.toString());
        CollectRecordEntity collectRecordEntity = collectRecordService.getCollectRecord(key, userId);
        AssertUtil.isEmpty(collectRecordEntity, "请勿重复收藏");

        // 收藏和增加记录
        applicationEventPublisher.publishEvent(new CollectEvent(this, key, userId, articleId, CollectEnum.ARTICLE.getType()));
    }

    @Override
    public void increaseLikeNum(Long articleId) {
        articleDao.increaseLikeNum(articleId);
    }

    @Override
    public void increaseCollectNum(Long articleId) {
        articleDao.increaseCollectNum(articleId);
    }

    @Override
    public void updateInfo(Long userId, ArticleUpdateReq req) {
        // 检查是否能更新
        ArticleEntity article = articleDao.getById(req.getArticleId());
        AssertUtil.isFalse(!article.getUid().equals(userId), "无权限");
        // 更新
        article = ArticleAdapter.updateArticle(article, req);
        articleDao.updateById(article);
        // 发布更新文章事件
        applicationEventPublisher.publishEvent(
                new ArticleUpdateEvent(this, article.getId(), req.getTagList(), req.getCategoryId(), userId,
                        req.getResourceList(), req.getIntegral(), req.getIsFree())
        );
    }

    @Override
    public ArticleUpdateInfoResp getArticleUpdateInfo(Long userId, ArticleInfoReq req) {
        Long articleId = req.getArticleId();
        ArticleEntity article = articleDao.getById(articleId);
        // 检查是否是本人
        AssertUtil.isTrue(article.getUid().equals(userId), "无权限");
        // 构造返回结果
        List<ResourceEntity> resourceList = resourceService.getByArticleId(articleId);
        ArticleUpdateInfoResp resp = ArticleAdapter.buildArticleUpdateResp(article, resourceList);

        return resp;
    }

    @Override
    public PageResp<ArticleInfoResp> adminArticleList(ArticleListReq req) {
        IPage<ArticleEntity> iPageList = articleDao.getArticleAdminList(req);
        if (iPageList.getTotal() == 0){
            return new PageResp<ArticleInfoResp>().empty();
        }

        return handleIPageList(iPageList);
    }

    @Override
    public void adminUpdateStatus(Long articleId, Integer status) {
        articleDao.updateStatus(articleId, status);
    }

    @Override
    public void reduceLikeNum(Long articleId) {
        articleDao.reduceLikeNum(articleId);
    }

    @Override
    public PageResp<ArticleInfoResp> getArticleCollectList(Long uid, ArticleCollectReq req) {
        // 分页查找收藏记录
        IPage<CollectRecordEntity> page = collectRecordService.getCollectRecordList(req, uid);
        // 没有收藏直接返回
        if (page.getTotal() == 0){
            return new PageResp<ArticleInfoResp>().empty();
        }
        // 获取文章ID列表
        List<Long> articleIds =  page.getRecords().stream()
                .map(item ->
                        Long.parseLong(item.getCollectObj().replace(CollectEnum.ARTICLE.getPrefix(), ""))
                )
                .collect(Collectors.toList());
        List<ArticleEntity> articleList = articleDao.listByIds(articleIds);
        // 组合返回
        IPage<ArticleEntity> resp = new Page<>(page.getCurrent(), page.getPages(), page.getTotal());
        resp.setRecords(articleList);

        return handleIPageList(resp);
    }

    @Override
    public void deleteCollectArticle(Long userId, Long articleId) {
        collectRecordService.deleteCollectRecord(userId, CollectEnum.getObjKey(CollectEnum.ARTICLE, articleId.toString()));
    }

    @Override
    public void deleteArticle(Long userId, Long articleId) {
        // 鉴权
        AssertUtil.isTrue(hasPrime(articleId, userId), "无权限");
        articleDao.removeById(articleId);
    }

    /**
     * 判断某个文章是否是用户的
     * @param articleId
     * @param userId
     * @return
     */
    boolean hasPrime(Long articleId, Long userId){
        ArticleEntity article = articleDao.getById(articleId);
        return article.getUid().equals(userId);
    }


    @Override
    public void updateStatus(Long userId, Long articleId, Integer status) {
        // 鉴权
        AssertUtil.isTrue(hasPrime(articleId, userId), "无权限");
        articleDao.updateStatus(articleId, status);
    }

    @Override
    public void saveArticleDraft(Long uid, ArticleSaveReq req) {
        ArticleDraftEntity articleDraft = BeanUtil.copyProperties(ArticleAdapter.buildArticleSave(req, uid), ArticleDraftEntity.class);
        articleDraft.setId(SnowFlakeUtil.nextId());
        articleDraftService.saveArticleDraft(articleDraft);
    }

    @Override
    public ArticleUpdateInfoResp getArticleDraftInfo(ArticleInfoReq req) {
        ArticleDraftEntity articleDraft = articleDraftService.getArticleDraft(req.getArticleId());
        List<ResourceEntity> resourceList = resourceService.getByArticleId(articleDraft.getId());
        ArticleUpdateInfoResp resp = ArticleAdapter.buildArticleUpdateResp(BeanUtil.copyProperties(articleDraft, ArticleEntity.class), resourceList);

        return resp;
    }

    @Override
    public PageResp<ArticleInfoResp> getArticleDraftList(LoginUser loginUser, ArticleListReq articleListReq) {
        return articleDraftService.getArticleDraftList(loginUser, articleListReq);
    }

    @Override
    public void updateDraftInfo(Long userId, ArticleUpdateReq req) {
        articleDraftService.updateDraft(userId, req);
    }

    @Override
    public void deleteDraftArticle(Long userId, Long articleId) {
        articleDraftService.deleteDraftArticle(userId, articleId);
    }

    @Override
    public PageResp<ArticleInfoResp> platerArticleList(LoginUser loginUser, ArticleListReq req) {
        // 获取拥有板块ID的集合
        List<Long> plateIds = userPlateRelationService.getPlateListByUId(loginUser.getUserId())
                .stream()
                .map(UserPlateRelationEntity::getPlateId)
                .collect(Collectors.toList());
        if (CollectionUtil.isEmpty(plateIds)){
            return new PageResp<ArticleInfoResp>().empty();
        }
        // 查询板块拥有的文章
        req.setPlateIds(plateIds);
        IPage<ArticleEntity> iPageList = articleDao.getArticleAdminList(req);
        if (iPageList.getTotal() == 0){
            return new PageResp<ArticleInfoResp>().empty();
        }

        return handleIPageList(iPageList);
    }
}
